<?php

namespace mbilling\BillingBundle\Services;

use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;

use mbilling\BillingBundle\Model\DomainsModel;
use mbilling\BillingBundle\Model\OrdersModel;
use mbilling\BillingBundle\Model\OrdersSharedModel;
use mbilling\BillingBundle\Model\UsersModel;
use mbilling\BillingBundle\Model\UsersBillsModel;

use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
class ACL
{
    protected $em;
    protected $aclProvider;
    protected $securityContext;
    protected $acl;
    
    public function __construct(EntityManager $em, $aclProvider, $securityContext)
    {
        $this->em = $em;
        $this->aclProvider = $aclProvider;
        $this->securityContext = $securityContext;
    }
    public function view()
    {
        print_r($this->acl);
    }
    public function createPermissionsForOne($object, $user, $mask=MaskBuilder::MASK_OWNER)
    {
        $securityIdentity = UserSecurityIdentity::fromAccount($user);
        $this->getObject($object, $securityIdentity);

        $objectAces = $this->acl->getObjectAces();
        $founded = 0;
        foreach ($objectAces as $index => $ace)
        {
            if ($securityIdentity->equals($ace->getSecurityIdentity()))
            {
                $founded++;
                try
                {
                    $this->acl->updateObjectAce($index, $mask);
                }
                catch (\OutOfBoundsException $exception)
                {
                    $this->acl->insertObjectAce($securityIdentity, $mask);
                }
            }
        }
        if ($founded == 0)
        {
            $this->acl->insertObjectAce($securityIdentity, $mask);
        }
        $this->aclProvider->updateAcl($this->acl);
    }
    public function createPermissionsForAll($object, $mask=MaskBuilder::MASK_OWNER)
    {
        $user_model = new UsersModel($this->em);
        $users = $user_model->findAll();
        foreach($users as $user)
        {
            $this->createPermissionsForOne($object, $user, $mask);
        }
    }
    public function createPermissionsForNewAdmin($currentUser)
    {
        $domain_model       = new DomainsModel($this->em);
        $order_model        = new OrdersModel($this->em);
        $orderShared_model  = new OrdersSharedModel($this->em);
        $user_model         = new UsersModel($this->em);
        $bill_model         = new UsersBillsModel($this->em);

        $domains      = $domain_model->findAll();
        $orders       = $order_model->findAll();
        $ordersShared = $orderShared_model->findAll();
        $users        = $user_model->findAll();
        $bills        = $bill_model->findAll();

        foreach ($domains as $domain)
            $this->createPermissionsForOne($domain, $currentUser);
        foreach ($orders as $order)
            $this->createPermissionsForOne($order, $currentUser);
        foreach ($ordersShared as $orderShared)
            $this->createPermissionsForOne($orderShared, $currentUser);
        foreach ($users as $user)
        {
            $this->createPermissionsForOne($user, $currentUser);
        }
        foreach ($bills as $bill)
            $this->createPermissionsForOne($bill, $currentUser);
    }
    public function createPermissionsForAllAdmins($object)
    {
        $user_model = new UsersModel($this->em);
        $admins = $user_model->findAllAdmins();
        foreach($admins as $admin)
        {
            $this->createPermissionsForOne($object, $admin);
        }
    }
    public function removePermissionsForOne($object, $user)
    {
        $securityIdentity = UserSecurityIdentity::fromAccount($user);
        $this->getObject($object, $securityIdentity);

        $objectAces = $this->acl->getObjectAces();
        foreach ($objectAces as $index => $ace)
        {
            if ($securityIdentity->equals($ace->getSecurityIdentity()))
            {
                $this->acl->deleteObjectAce($index);
            }
        }
        $this->aclProvider->updateAcl($this->acl);
    }
    public function removePermissionsForExAdmin($currentUser)
    {
        $domain_model       = new DomainsModel($this->em);
        $order_model        = new OrdersModel($this->em);
        $orderShared_model  = new OrdersSharedModel($this->em);
        $user_model         = new UsersModel($this->em);
        $bill_model         = new UsersBillsModel($this->em);

        $domains      = $domain_model->findAll();
        $orders       = $order_model->findAll();
        $ordersShared = $orderShared_model->findAll();
        $users        = $user_model->findAll();
        $bills        = $bill_model->findAll();

        foreach ($domains as $domain)
            $this->removePermissionsForOne($domain, $currentUser);
        foreach ($orders as $order)
            $this->removePermissionsForOne($order, $currentUser);
        foreach ($ordersShared as $orderShared)
            $this->removePermissionsForOne($orderShared, $currentUser);
        foreach ($users as $user)
            $this->removePermissionsForOne($user, $currentUser);
        foreach ($bills as $bill)
            $this->removePermissionsForOne($bill, $currentUser);
        $this->createPermissionsForOne($currentUser, $currentUser);
    }
    public function removePermissionsForAllAdmins($object)
    {
        $user_model = new UsersModel($this->em);
        $admins = $user_model->findAllAdmins();
        foreach($admins as $admin)
        {
            $this->removePermissionsForOne($object, $admin);
        }
    }
    public function checkPermissions($object, $mask)
    {
        if (false === $this->securityContext->isGranted($mask, $object))
        {
            throw new AccessDeniedException();
        }
    }
    public function getObject($object, $securityIdentity)
    {
        $objectIdentity = ObjectIdentity::fromDomainObject($object);
        try 
        {          
            $this->acl = $this->aclProvider->findAcl($objectIdentity, array($securityIdentity));
        }
        catch (AclNotFoundException $exception)
        {
            $this->acl = $this->aclProvider->createAcl($objectIdentity);
        }
    }
}
